#pragma once
#include <iostream>
#include <vector>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <exception>
#include <mutex>
#include <atomic>
#include "Util.hpp"
#include <jsoncpp/json/json.h>
#include "/usr/include/mysql/mysql.h"
#include "/usr/include/mysql++/mysql++.h"
/*model完成将数据从硬盘提取到内存，从内存写到硬盘，响应要求显示的数据*/

struct Question {
    /*概要情况*/
    int ProblemID;/*题目ID(string方便)*/
    std::string ProblemName;/*题目名称*/
    int Difficulty;/*题目难度*/
    /*详细情况*/
    std::string Description;/*描述*/
    int AttemptedTimes;/*尝试次数*/
    int AcceptedTimes;/*成功次数*/
    std::vector<std::string> Tags;/*题目标签*/
    int TestNum;/*题目对应拥有的测试集个数*/
};

/*调用数据库接口的封装(单例)*/
class MysqlObj{
    public:
      MysqlObj(const MysqlObj& ths) = delete;
      MysqlObj& operator=(const MysqlObj& ths) = delete;
      ~MysqlObj() {
          conn.disconnect();
          std::cout << "~MysqlObj()" << std::endl;
      }
      static MysqlObj& GetInstance() {
            return _mysqlObj;
      }
      /*使用前先连接,构造函数里最好不要做有风险的事*/
      void MysqlObjInitConnection() throw (std::string) {
        // 连接数据库
        if (conn.connect(db, server, user, password))
        {
           LOG(INFO, "connect db succeed.");
        }
        else
        {
           /*抛异常*/
           LOG(INFO, "connect db fail.");
           throw(std::string("connect db failed"));
        }
      }
      /* SELECT * FROM Problem */
      void SelectAllQuestionsFromSql(std::map<int, Question>& qesCache) {
           // 查询操作，查询结果保存在 query 中
           mysqlpp::Query query = conn.query("select * from Problem");
           // 处理并打印查询结果
           if (mysqlpp::StoreQueryResult res = query.store())
           {
               // 输出数据左对齐
               std::cout.setf(std::ios::left);
               // 字段宽度为20位
               // 打印表的字段名
              // std::cout << std::setw(21) << "id" <<
              //         std::setw(21) << "name" <<
              //         std::setw(21) << "difficulty" <<
              //         std::endl;
               // 打印查询结果
               mysqlpp::StoreQueryResult::const_iterator it;
               for (it = res.begin(); it != res.end(); ++it)
               {
                   mysqlpp::Row row = *it;
                   Question tmp;
                   //1.作者提供了operator int()和转化成字符串的接口,但是转化成字符串的接口是void mysqlpp::String::to_string(std::string& s) ;
                  // tmp.ProblemID = mysqlpp::String(row[0]); 
                   tmp.ProblemID = row[0];
                   row[1].to_string(tmp.ProblemName);
                   tmp.Difficulty = row[2];
                   tmp.Description = row[3].c_str();/*或者直接提取char*来进行string的构造*/
                   tmp.AttemptedTimes = row[4];
                   tmp.AcceptedTimes = row[5];
                   tmp.TestNum = row[6];
                   qesCache[tmp.ProblemID]= tmp; 
               }
           }
      }
      /*Select Count(*) from Problem(获取元组数量)*/
      int SelectCountAllFromProblem() {
           try{
                 int size = conn.count_rows("Problem");
                 LOG(INFO, "Problem size = " + std::to_string(size));
              // mysqlpp::Query query = conn.query("select count(*) from Problem");
              // mysqlpp::StoreQueryResult res = query.store();
              // mysqlpp::StoreQueryResult::const_iterator it;
              // for (it = res.begin(); it != res.end(); ++it) {
              //      mysqlpp::Row row = *it;
              //      return row[0];
              // }
                 return size;
           }
           catch(mysqlpp::Query e) {
                std::cout << "数据库操作失败:" << e.error() << std::endl;
           }
      } 
      /*获取用户名和密码*/
      void SelectAllFromAccount(std::unordered_map<std::string, std::string>& AccountMap) {
           try {
               mysqlpp::Query query = conn.query("select * from Account");
               mysqlpp::StoreQueryResult res = query.store();
               mysqlpp::StoreQueryResult::const_iterator it;
               for (it = res.begin(); it != res.end(); ++it) {
                    mysqlpp::Row row = *it;
                    AccountMap[row[0].c_str()] = row[1].c_str();
               }
           }
           catch(mysqlpp::Query e) {
                std::cout << "数据库操作失败:" << e.error() << std::endl;
           }
      } 
      /*Update Table Problem*/
      /*用于和mysqluse.cc配合,mysqluse.cc可以读取文件中的题目内容和题目的数据集个数*/
      void UpdateProblemTable(const int& Id, const std::string& colName, const std::string& context) {
           std::string ID = std::to_string(Id); 
           LOG(INFO, "ProblemID is " + ID);
           try{
               std::string mysqloperations = "update Problem set " + colName + " = '" + context + "' where ProblemID = " + ID;
               LOG(INFO, mysqloperations);
               mysqlpp::Query query = conn.query(mysqloperations);
               query.execute();
           }
           catch(mysqlpp::Query e) {
                std::cout << "数据库操作失败:" << e.error() << std::endl;
           }
      }
      /*mysqluse.cc模块更新题目和测试集数量*/
      void InsertProblemTable(const int& Id, const std::string& problemName, const std::string& problemDifficulty, const std::string& problemDesc, const std::string& TestNum) {
           try{
               std::string ID = std::to_string(Id);
               std::string mysqloperations = "insert into Problem values (" + ID + ",'"+ problemName + "'," + problemDifficulty + ",'" + problemDesc + "'," +  "0,0," + TestNum + ")"; 
               LOG(INFO, mysqloperations);
               mysqlpp::Query query = conn.query(mysqloperations);
               query.execute();
           }
           catch(mysqlpp::Query e) {
                std::cout << "数据库操作失败:" << e.error() << std::endl;
           }
      }

      /*数据库插入用户*/
      bool MysqlInsertUser(const std::string& email, const std::string& password) {
            std::string mysqloperations = "insert into Account values('" + email + "'," + "'" + password  + "'" + ",0)";
            LOG(INFO, mysqloperations);
            mysqlpp::Query query = conn.query(mysqloperations);
            query.execute();
      }
      /*数据库查询History总记录,服务器加载的时候查询一次,之后直接使用缓存里的atomic<int>,降低每次O(n)[Innodb引擎]的查询*/
      int QueryHistorySize() {
           int totalsize = conn.count_rows("History");
           return totalsize;
      }
      /*数据库查询10条History记录表*/
      int QueryHistory(const std::string& type, Json::Value& v, const int& totalsize, int top, std::string username = "") {
           LOG(INFO, "top = " + std::to_string(top));
           if (top <= 0) top = totalsize;/*当成新进入status页面,top赋初值*/
           /*防止越界*/
           LOG(INFO, "top = " + std::to_string(top));
           int ed, up;
           if (type == "next" || type == "init") {
              ed = std::max(top, 0);  /*下指针*/
              up = std::max(0, ed - 10); /*上指针*/
           }
           else if (type == "prev") {
              ed = std::min(top, totalsize);  /*下指针*/
              up = std::max(0, std::min(totalsize, ed - 10)); /*上指针*/
              if (up == totalsize) { /*特判首页prev*/
                  up = std::max(0, top - 10);
              }
           }
           try {
                LOG(INFO, "up = " + std::to_string(up));
                mysqlpp::Query query = conn.query("select * from History limit " + std::to_string(up) + ",10"); /*每次查询10行*/
                LOG(INFO, "首次查询:select * from History limit " + std::to_string(up) + ",10");
                mysqlpp::StoreQueryResult res;
                res = query.store();
                if (username != "") {
                    query = conn.query("select * from History where User = '" + username +  "' limit " + std::to_string(up) + ",10"); /*每次查询10行*/
                    res = query.store();
                    LOG(INFO, "特定查询:select * from History where User ='" + username + "' limit " + std::to_string(up) + ",10");
                }
                mysqlpp::StoreQueryResult::const_reverse_iterator it;
                LOG(INFO, "res.size()=" + std::to_string(res.size()));
                int id = 1;
                Json::Value arrObj;
                for (it = res.rbegin(); it != res.rend(); ++it, ++id) {
                     mysqlpp::Row row = *it;
                     Json::Value tmp;
                     tmp["RunID"] =  std::to_string(row[0].operator int());
                     tmp["User"] = row[1].c_str();
                     tmp["ProblemID"] = std::to_string(row[2].operator int());
                     tmp["SubmitTime"] = row[3].c_str(); /*时间戳*/
                     tmp["FilePath"] = row[4].c_str();
                     tmp["Status"] = row[5].c_str();
                     //LOG(INFO, tmp.toStyledString());
                     arrObj.append(tmp); 
                }
                v["array"] = arrObj;
                LOG(INFO, v.toStyledString());
           }
           catch (mysqlpp::Query e) {
                std::cout << "数据库操作失败:" << e.error() << std::endl;
                return -1;
           }
           if (ed == totalsize) { /*说明是初始化*/
              return totalsize;
           }
           else {
             if (type == "init" || type == "next") {
                return up;
             }
             else return ed;
           }
      }
      bool InsertIntoHistory(const Json::Value& v) {
           std::string mysqloperations = "insert into History(User,ProblemID,FilePath,Status) values('" + v["User"].asString() + "','" + v["ProblemID"].asString() + "','" + v["FilePath"].asString() + "','" + v["Status"].asString()  + "')";
           LOG(INFO, mysqloperations);
           mysqlpp::Query query = conn.query(mysqloperations);
           query.execute();
           return true;
      }
      /*动态查询用户的历史记录总数*/
      int QueryUserHistory(std::string username) {
           mysqlpp::Query query = conn.query("select count(*) from Account where email = '" + username + "'" );
           LOG(INFO, "select count(*) from Account where User = '" + username + "'" );
           mysqlpp::StoreQueryResult res = query.store();
           mysqlpp::StoreQueryResult::const_iterator it;
           for (it = res.begin(); it != res.end(); it++) {
                mysqlpp::Row row = *it;
                return row[0];
           }
      }
      private:
         MysqlObj() 
           :db("oj_problems"),
           server("localhost"),
           user("ojuser"),
           password("123456"),
           conn(false)
      {

      }

      private:
         const char* db;
         const char* server;
         const char* user;
         const char* password;
         mysqlpp::Connection conn; // 创建数据库连接
         static MysqlObj _mysqlObj;/*创建单例对象*/
};
MysqlObj MysqlObj::_mysqlObj;


class Oj_Model{
    private:
      std::mutex _mutex;
      std::map<int, Question> _questionCache;
      std::unordered_map<std::string, std::string> _accountMap;/*数据库中的注册用户*/
      std::atomic<int> _historyTotalSize;
    public:
      Oj_Model() = default;
      /*加载数据库缓存到内存中,如果出现更新要执行对应函数重新加载到内存*/
      bool Load() {
        try{
            MysqlObj& _mysqlObj  = MysqlObj::GetInstance();
            _mysqlObj.MysqlObjInitConnection();
            _mysqlObj.SelectAllQuestionsFromSql(_questionCache);
            for (auto& i:_questionCache) {
                   std::cout << std::setw(21) << i.second.ProblemID << ' ' <<
                           std::setw(21) << i.second.ProblemName << ' ' <<
                           std::setw(21) << i.second.Difficulty << ' ' <<
                           std::setw(21) << i.second.Description << ' ' <<
                           std::setw(21) << i.second.AttemptedTimes << ' ' <<
                           std::setw(21) << i.second.AcceptedTimes << ' ' <<
                           std::setw(21) << i.second.TestNum << ' ' <<
                           //tags之后建写好表再写
                           std::endl;
            }
            _mysqlObj.SelectAllFromAccount(_accountMap);
            for (auto& i:_accountMap) {
                   std::cout << std::setw(21) << i.first << ' ' <<
                           std::setw(21) << i.second << ' ' <<
                           std::endl;
            }
            _historyTotalSize =  _mysqlObj.QueryHistorySize();

        }
        catch(const std::string& str) {
            std::cout << "异常为:" << str << std::endl;
        }
        catch(...) {
            std::cout << "未知异常" << std::endl;
        }
      }
      /*获取整个题目主页概要*/
      bool GetAllQuestions(std::vector<Json::Value>& v) {
           for (auto& i: _questionCache) {
                Json::Value res;
                res["ProblemID"] = i.second.ProblemID;
                res["ProblemName"] = i.second.ProblemName;
                res["Difficulty"] = i.second.Difficulty;
                v.push_back(res);
           }
           return true;
      }
      /*通过题目id获得具体题目的主要内容*/
      bool GetOneQuestion(const std::string& sid, Json::Value& res) {
           int id = atoi(sid.c_str());
           LOG(INFO, "id = " + std::to_string(id));
           res["ProblemID"] = std::to_string(id);
           res["ProblemName"] = _questionCache[id].ProblemName;
           res["Difficulty"] = _questionCache[id].Difficulty;
           res["Description"] = _questionCache[id].Description;
           res["AttemptedTimes"] = _questionCache[id].AttemptedTimes;
           res["AcceptedTimes"] = _questionCache[id].AcceptedTimes;
           res["TestNum"] = _questionCache[id].TestNum;
           return true;
      }
      /*通过题目ID获得本次提交的内容*/
      bool GetQuestionTestStatus(const std::string& sid, Json::Value& res) {

      }
      /*用户相关的内容等后期再加用户的接口,再调用下方接口*/
      
      /*注册用户,添加数据库,内存中的数据库备份要更新*/
      bool AccountInsert(const std::string& email, const std::string& password) {
            std::unique_lock<std::mutex> uiq(this -> _mutex); /*线程安全*/
            if (_accountMap.count(email)) {
                return false;
            }
            _accountMap.insert({email, password});/*更新缓存*/
            uiq.unlock();
            /*进行增删改时,Mysql会默认加上写锁*/
            MysqlObj& _mysqlObj = MysqlObj::GetInstance();
            _mysqlObj.MysqlInsertUser(email, password);
            return true;
      }
      /*将用户账号密码登录是否正确,返回主页*/
      bool UserLogin(const std::string& email, const std::string& password) {
            std::lock_guard<std::mutex> lgd(this -> _mutex); /*防止读时发生注册写导致的扩容——线程安全*/
            if (_accountMap.count(email) && _accountMap[email] == password)  {
              return true;
            }
            else return false;
      }
      /*Logout退出时调用前端ajax删除cookie*/
      /*获得缓存中的提交总数*/
      int GetHistorySize(std::string name = "") {
          if (name == ""){
              return _historyTotalSize;
          }
          else {
              MysqlObj& _mysqlObj = MysqlObj::GetInstance();
              return _mysqlObj.QueryUserHistory(name);
          }
      }
      /*增加缓存中的提交总数*/
      bool AddHistorySize() {
           _historyTotalSize++;
      }
      bool HistoryInsert(const Json::Value& insertV) {
           /*进行写时Mysql会默认加上写锁*/
           MysqlObj& _mysqlObj = MysqlObj::GetInstance();
           _mysqlObj.InsertIntoHistory(insertV);
           return true;
      }
      /*调用mysql查找History记录*/
      int HistoryQuery(const std::string& type, Json::Value& que, int top, std::string name = "") {
           /*进行读时,Mysql会默认加上共享锁*/
           MysqlObj& _mysqlObj = MysqlObj::GetInstance();
           return _mysqlObj.QueryHistory(type, que, _historyTotalSize, top, name);
      }
};
